package pl.edu.agh.student.vrp;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jgap.BaseGeneticOperator;
import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.IChromosome;
import org.jgap.InvalidConfigurationException;
import org.jgap.Population;
import org.jgap.impl.IntegerGene;
import pl.edu.agh.student.vrp.utils.ChromosomeUtils;

public class MyCrossover extends BaseGeneticOperator
{
    private Random r;
    private GA ga;
    
    public MyCrossover(Configuration a_configuration) throws InvalidConfigurationException
    {
        super(a_configuration);
        r =new Random();
    }

    public void operate(Population pltn, List list)
    {
        
        int size = pltn.size();
        for(int i_1 = 0; i_1 < size; i_1++){
            int i_2 = r.nextInt(size);//Math.min(getConfiguration().getPopulationSize(), size);
            
            IChromosome chrom1 = pltn.getChromosome(i_1);
            IChromosome chrom2 = pltn.getChromosome(i_2);
            
            IChromosome I1 = (IChromosome)chrom1.clone();
            IChromosome I2 = (IChromosome)chrom2.clone();
            
           
            Gene[] sr = getSubRoute(I2);    
            int a1 = ((IntegerGene)sr[0]).intValue();
            
            int c = getCloser(a1, sr);
            
            List<Gene[]> l = new LinkedList<Gene[]>();
            
            for(Gene[] route: ChromosomeUtils.getRoutes(I1, ga.getDestinations().size(), ga.getTotalVehicles())){
                Gene[] newRoute = removeDupl(route, sr, ga.getDestinations().size()-1);
                l.add( newRoute );
            }
            
            insertSR(l, c, sr);
            
            try
            {
                IChromosome newCh = createNewChromosome(l, I1.size());
                list.add(newCh);
                list.add(chrom1);
            }
            catch (Exception ex)
            {
                Logger.getLogger(MyCrossover.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            
            // From the genetic material of I2 randomly select a sub-route SR
                // Find the customer c, not belonging to SR, that is geographically closer to a1
                // From the original genetic material of I1 remove all duplicated customers that also appear on SR, obtaining a descendant D
                // From the original genetic material of I1 remove all duplicated customers that also appear on SR, obtaining a descendant D
        }
        //throw new UnsupportedOperationException("Not supported yet.");
    }
    
    public IChromosome createNewChromosome(List<Gene[]> I, int size) throws Exception{
        Gene[] genes = new Gene[size];
        int k = 0;
        for (int i = 0; i < I.size(); i++) {
            Gene[] gene = I.get(i);
            for(int j=0; j<gene.length; j++){
                genes[k++] = gene[j];
            }
            IntegerGene sep = new IntegerGene(super.getConfiguration(), 0, 0);
            sep.setAllele(new Integer(0));
            genes[k++] = sep;
        }

        for (int i = k; i < size; i++) {
            IntegerGene sep = new IntegerGene(super.getConfiguration(), 0, 0);
            sep.setAllele(new Integer(0));
            genes[k++] = sep;
        }
        
        IChromosome sample = new Chromosome(super.getConfiguration(), genes);
        
        //System.out.println("New Chromosome");
//        for(Gene g: sample.getGenes()){
//            IntegerGene ig = (IntegerGene) g;
//            System.out.print(ig.intValue()+", ");
//        }
        return sample;
    }
    
    public Gene[] getSubRoute(IChromosome I){
        List<Gene[]> routes = ChromosomeUtils.getRoutes(I, ga.getDestinations().size(), ga.getTotalVehicles());
        if(routes.size() < 2){
            System.out.println("Number of routes: "+routes.size());
            for(int j=0; j<routes.get(0).length; j++){
            IntegerGene ig = (IntegerGene)routes.get(0)[j];
            System.out.print(ig.intValue() + ", ");
            }
            System.out.println();
        }
        
        int rid1 = r.nextInt(routes.size());
        
        //System.out.println("Selected route : "+rid1);
        //System.out.println("Selected route length: "+routes.get(rid1).length);
        
        int s = r.nextInt(routes.get(rid1).length);
        int e = r.nextInt(routes.get(rid1).length-s)+s;

        //System.out.println("Start: "+s);
        //System.out.println("End: "+e);
        
        Gene[] route = routes.get(rid1);
        Gene[] sr = new Gene[e - s + 1];
        
        //System.out.println("Subroute length: "+sr.length);
        for(int i = s; i <= e; i++){
            sr[i-s] = route[i];
        }
        
        //System.out.println("subroute");
        for(int i=0; i<sr.length; i++){
            IntegerGene ig = (IntegerGene)sr[i];
            //System.out.print(ig.intValue() + ", ");
        }
        //System.out.println();
        
        return sr;
    }
    
    public int getCloser(int a1, Gene[] sr){
        
        Set<Integer> customersOnSR = new TreeSet<Integer>();
        for(int i=0; i<sr.length; i++){
            IntegerGene ig = (IntegerGene)sr[i];
            customersOnSR.add(ig.intValue());
        }
        
        Destination c1 = null;
        for(Destination d: ga.getDestinations()){
            int cid = d.getCustomerNo() -1;
            if(d.getCustomerNo() == a1){
                c1 = d;
                break;
            }
        }
        Destination x = null;
        Double min = Double.MAX_VALUE;

        for(Destination c2: ga.getDestinations()){
            
            int cid = c2.getCustomerNo()-1;
            if( cid != 0 && !customersOnSR.contains(cid)){
                Double distance = (c1.getxCoordinate() - c2.getxCoordinate())*(c1.getxCoordinate() - c2.getxCoordinate()) +
                        (c1.getyCoorginate() - c2.getyCoorginate())*(c1.getyCoorginate() - c2.getyCoorginate());
                if(distance < min){
                    x = c2;
                }
            }
        }
        
        return x.getCustomerNo()-1;
        
        
        //int c = r.nextInt(10)+1;
        //System.out.println("Closer customer id to "+a1+": "+c);
        //return c;
    }
    
    public void insertSR(List<Gene[]> I, int c, Gene[] sr){
        Gene[] newRoute = null;

        List<Gene[]> I2 = new LinkedList<Gene[]>();
        for(int j=0; j<I.size(); j++){
            
            Gene[] route = I.get(j);
            if( contain(c, route) ){
               newRoute = new Gene[route.length + sr.length];
               
               //System.out.println("New route lenght: "+newRoute.length);
               
               int position = -1;
               for(int i=0; i<route.length; i++){
                   IntegerGene ig = (IntegerGene)route[i];
                   if(ig.intValue() == c){
                       position = i;
                       break;
                   }
               }
               
               //System.out.println("Position: "+position);
               for(int i=0; i<=position; i++){
                   newRoute[i] = route[i];
               }
               
               for(int i=0; i<sr.length; i++){
                   newRoute[i + position + 1] = sr[i];
               }
               
               for(int i=position+1; i<route.length; i++){
                   newRoute[i + sr.length] = route[i];
               }
               
               List<Gene> validRoute = new LinkedList<Gene>();
               double S=0.0;
               for(int i=0; i<newRoute.length; i++){
                   IntegerGene ig = (IntegerGene)newRoute[i];
                   S += ga.getDestinations().get(ig.intValue()).getDemand();
                   
                   if(S > ga.getVehicleCapacity()){
                       
                       Gene[] tmp = new Gene[validRoute.size()];
                       validRoute.toArray(tmp);
                       I2.add( tmp );
                       S = 0.0;

                       S += ga.getDestinations().get(ig.intValue()).getDemand();
                       validRoute = new LinkedList<Gene>();
                   }
                   
                   validRoute.add(newRoute[i]);
               }
               
               Gene[] tmp = new Gene[validRoute.size()];
               validRoute.toArray(tmp);
               I2.add( tmp );

               //I.set(j, newRoute);
               //I2.add(newRoute);
           }
           else{
                I2.add(route);
           }
        }
        //System.out.println("I: "+I2.size());
        //I = I2;
        I.clear();
        I.addAll(I2);
        
//        for(int i=0; i < newRoute.length; i++){
//            IntegerGene ig = (IntegerGene)newRoute[i];
//            //System.out.print(ig.intValue()+", ");
//        }
//        //return newRoute;
    }
    
    private boolean contain(int c, Gene[] route){
        for(int i=0; i<route.length; i++){
            IntegerGene ig = (IntegerGene)route[i];
            
            //System.out.println(ig.intValue()+" = "+c);
            
            if(ig.intValue() == c){
                //System.out.println("True: "+c);
                return true;
            }
        }
        return false;
    }
    
    public Gene[] removeDupl(Gene[] route, Gene[] sr, int max){
        List<Gene> g = new LinkedList<Gene>();
        List<Integer> l = new LinkedList<Integer>();
        
        for(int i=0; i<route.length; i++){
            IntegerGene ig = (IntegerGene)route[i];
            l.add(new Integer(ig.intValue()));
        }
        
        //System.out.println("RemoveDuplicate: Route: "+l);
        
        for(int i=0; i<sr.length; i++){
            IntegerGene ig = (IntegerGene)sr[i];
            l.remove(new Integer(ig.intValue()));
        }
        
        //System.out.println("RemoveDuplicate: New Route: "+l);
        
        Gene[] newRoute = new Gene[l.size()];
        for(int i=0; i<l.size(); i++){
            try
            {
                Gene x = new IntegerGene( super.getConfiguration() , 1, max);
                x.setAllele(new Integer(l.get(i)));
                newRoute[i] = x;
            }
            catch (InvalidConfigurationException ex)
            {
                Logger.getLogger(MyCrossover.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        return newRoute;
    }

    public int compareTo(Object t)
    {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void setGa(GA ga)
    {
        this.ga = ga;
    }
    
}
